package com.sxt.server.server;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
import com.sxt.server.handler.MessageConsumer;
import com.sxt.server.handler.MessageConsumerImpl;
import com.sxt.server.init.JT808ChannelInitializer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class TCPServer {
	/**
	 * NioEventLoopGroup是一个处理I / O操作的多线程事件循环。 Netty为不同类型的传输提供各种EventLoopGroup实现。
	 * 我们在此示例中实现了服务器端应用程序，因此将使用两个NioEventLoopGroup。
	 * 第一个，通常称为“老板”，接受传入连接。第二个，通常称为“工人”， 一旦老板接受连接并将接受的连接注册到工作人员，就处理被接受连接的流量。
	 * 使用了多少个线程以及它们如何映射到创建的Channels取决于EventLoopGroup实现，甚至可以通过构造函数进行配置。
	 */

	@Value("${netty.port}")
	private int nettyPort;

	@Value("${netty.read-timeout}")
	private int readTimeOut;

	@Autowired
	private JT808ChannelInitializer jt808ChannelInitializer;
	//
	private EventLoopGroup bossGroup = null;
	//
	private EventLoopGroup workerGroup = null;
	//
	private ChannelFuture future;

//	@Autowired
//    @Qualifier("businessGroup")
//    private EventExecutorGroup businessGroup;

	/**
	 * 	启动服务：
	 * 1.先启动Disruptor服务 
	 * 2.在启动netty服务
	 *
	 * @return
	 * @throws InterruptedException
	 */
	@PostConstruct
	public void init() {
		disruptorStart();
		tcpStart();
	}
	/**
	 * 	启动Disruptor服务
	 */
	public void disruptorStart() {
		MessageConsumer[] conusmers = new MessageConsumer[4];
		for (int i = 0; i < conusmers.length; i++) {
			MessageConsumer messageConsumer = new MessageConsumerImpl("code:serverId:" + i);
			conusmers[i] = messageConsumer;
		}
		RingBufferWorkerPoolFactory.getInstance().initAndStart(ProducerType.MULTI, 1024 * 1024,
				// new YieldingWaitStrategy(),
				new BlockingWaitStrategy(), conusmers);
	}
	/**
	 * 	启动netty服务
	 */
	public void tcpStart() {
		// 解决netty网关启动之后，cpu使用率达到了100%
		System.setProperty("org.jboss.netty.epollBugWorkaround", "true");

		ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);// 内存泄漏检测 开发推荐PARANOID 线上SIMPLE
		// 内存泄漏检测 开发推荐 PARANOID 线上使用默认的SIMPLE即可
		// ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase()));
		try {

			/** 针对linux优化 等同于 boolean isEpollAvailable = Epoll.isAvailable(); */
			boolean isEpollAvailable = Epoll.isAvailable();
			/** 配置服务端的NIO线程组 */
			if (isEpollAvailable) {
				/** 负责服务端接收客户端的连接 */
				this.bossGroup = new EpollEventLoopGroup();
				/** 负责进行SocketChannel的网络读写 */
				this.workerGroup = new EpollEventLoopGroup();
			} else {
				/** 负责服务端接收客户端的连接 */
				this.bossGroup = new NioEventLoopGroup();
				/** 负责进行SocketChannel的网络读写 */
				this.workerGroup = new NioEventLoopGroup();
			}

			// =============================分割线===================================//
			/** Netty启动Nio服务端的辅助类 */
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap.group(this.bossGroup, this.workerGroup);
			/** Linux平台用Epoll模式 **/
			if (isEpollAvailable) { // Linux平台用Epoll模式
				bootstrap.channel(EpollServerSocketChannel.class);
			} else {
				bootstrap.channel(NioServerSocketChannel.class);
			}

			bootstrap.option(ChannelOption.SO_BACKLOG, 1024) // 服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
					.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)// 缓存区 池化操作
					.childOption(ChannelOption.TCP_NODELAY, true)// 立即写出
					.childOption(ChannelOption.SO_KEEPALIVE, true)// 长连接
					.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)// 缓存区 池化操作 关键是这句
					.childHandler(jt808ChannelInitializer);// 不能new ，负责导致JT808ChannelInitializer参数无法注入

			/** 绑定端口同步操作 */
			this.future = bootstrap.bind(this.nettyPort).sync();

			if (future.isSuccess()) {
				log.info("启动 Netty 成功");
			}
			/** 注意： 监听端口关闭(//等待客户端链路关闭,导致websocket无法启动) **/
			// future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			bossGroup.shutdownGracefully().syncUninterruptibly();
			workerGroup.shutdownGracefully().syncUninterruptibly();
			e.printStackTrace();
		}

	}

	/**
	 * 	销毁启动netty服务
	 * 
	 * @throws InterruptedException
	 */
	@PreDestroy
	public void destroy() throws InterruptedException {
		this.future.channel().closeFuture().sync();
		this.bossGroup.shutdownGracefully().syncUninterruptibly();
		this.workerGroup.shutdownGracefully().syncUninterruptibly();
		log.info("关闭 Netty 成功");
	}
}
/**
 * public class UdpServer {
 *
 * UDP没有连接，只监听端口 UDP 无法从Channel获取远程客户端的ip和端口号，
 * 而是通过发过来的DatagramPacket中的sender获取发送消息客户端的ip和端口号 UDP不需要粘包拆包，每个包都是完整的 netty
 * UDP一般接收的是DatagramPacket包，里面封装了消息对象
 *
 * public void run(int port) throws Exception { NioEventLoopGroup group = new
 * NioEventLoopGroup(); Bootstrap bootstrap = new
 * Bootstrap();//udp不能使用ServerBootstrap
 * bootstrap.group(group).channel(NioDatagramChannel.class)//udp通道
 * .option(ChannelOption.SO_BROADCAST, true) //允许发送广播 .handler(new
 * UdpServerHandler());//服务端业务处理 //绑定端口 //等待端口关闭
 * bootstrap.bind(port).sync().channel().closeFuture().sync(); }
 * 
 * public static void main(String[] args) throws Exception { new
 * UdpServer().run(9527);//绑定端口,启动服务 new UdpServer().run(9528); } }
 */